#include "AI.h"
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <string>
#include <iostream>
#include <ctime>

using namespace std;

// stores if each square is blocked

void testPerformance(Board & b, BoardState player)
{
    const int REPEAT = 10000;
    static clock_t sum1 = 0;
    static clock_t sum2 = 0;
    clock_t start;
    int count1, count2;
    int moves[100];
    BitBoard effect[100];
    
    gameState gs = parseBoard(b, BLOCKED_MAP);

    start = clock();
    for (int i = 0; i < REPEAT; i++)
    {
        const set<pair<int, int> > moves = b.legalMoves(player);
        count1 = moves.size();
        
        set<pair<int, int> >::const_iterator it = moves.begin();
        while (it != moves.end()) {
            Board temp = Board(b);
            temp.makeMove(it->first, it->second, player);
            it++;
        }
    }
    sum1 = sum1 + clock() - start;
    
    start = clock();
    for (int i = 0; i < REPEAT; i++)
    {
        count2 = getMoves(gs, BLOCKED_MAP, player, moves, effect);
        
        for (int j = 0; j < count2; j++)
        {
            gameState temp = applyMove(gs, player, moves[j], effect[moves[j]]);
        }
    }
    sum2 = sum2 + clock() - start;
    
    
    // check the number of moves is the same
    assert(count1 == count2);
    
    // check the resulting board are the same
    const set<pair<int, int> > move_pairs = b.legalMoves(player);
    count2 = getMoves(gs, BLOCKED_MAP, player, moves, effect);
    set<pair<int, int> >::const_iterator it = move_pairs.begin();
    
    while (it != move_pairs.end())
    {
        Board temp = Board(b);
        temp.makeMove(it->first, it->second, player);
        
        int index = it->first + it->second * 10;
        
        // assert each move is the same
        bool isIn = false;
        for (int i = 0; i < count2; i++)
        {
            if (moves[i] == index)
                isIn = true;
        }
        assert(isIn);
        
        gameState temp_gs = applyMove(gs, player, index, effect[index]);
        
        gameState temp_gs2 = parseBoard(temp, BLOCKED_MAP);
        
        assert(temp_gs == temp_gs2);
        it++;
    }
    
    cout << "array representation:     " << sum1 << endl;
    cout << "bit board representation: " << sum2 << endl;
}

/* test the agent
   #in#  b          ... current board state
   #in#  player     ... current player                                      */
void testClient(Board & b, BoardState player)
{
    //testPerformance(b, player);
    return;
    
    bool _gameEnd = false;
    int _moves[100], i, j;
    BitBoard _effect[100];

    gameState gs = parseBoard(b, BLOCKED_MAP);
    
    i = j = -1;
    
    while (!_gameEnd)
    {
        printGameState(gs, BLOCKED_MAP, 1);
        int mCount = getMoves(gs, BLOCKED_MAP, player, _moves, _effect);
        
        mCount = evaluateMobility(gs, player);
        
        BitBoard empty = ~(gs.black | gs.white | BLOCKED_MAP);
        
        cout << "white frontier: " << evaluateFrontier(gs.white, empty) << endl;
        cout << "black frontier: " << evaluateFrontier(gs.black, empty) << endl;
        
        cout << mCount << " possible moves.\n";
        
        for (int t = 0; t < mCount; t++)
        {
            cout << "valid move: " << _moves[t] / 10 << ", " << _moves[t]%10 << endl;
            
            //cout << "effect:" << endl;
            
            //printBitBoard(_effect[_moves[t]], 2);
        }
        
        int _m = -1;
        while (!validMove(_m, _moves, mCount))
        {
            cout << "Give next move (row column format): ";
            cin >> i >> j;
            _m = i * 10 + j;
        }
        
        gs = applyMove(gs, player, _m, _effect[_m]);
        
        player = player == BLACK ? WHITE : BLACK;
    }

}

int NegaScout(gameState node, BoardState player, int depth, int alpha, int beta, int stage, bool terminal)
{
    if (stage == 92)
    {
        if (player == WHITE)
            return evaluateTerminal(node);
        else
            return -evaluateTerminal(node);
    }
    if (depth == 0)
    {
        int value = evaluateGame(node, player, stage);
        if (player == WHITE)
            return value; 
        else
            return -value; 
    }
    
    int _moves[100];
    BitBoard _effects[100];
    
    int _mCount = getMoves(node, BLOCKED_MAP, player, _moves, _effects);
    int b = beta;    
    BoardState next_player = player == BLACK ? WHITE : BLACK;
    
    // if current player has no move
    if (_mCount == 0)
    {
        // neither player has move, we reached terminal position
        if (terminal)
        {
            if (player == WHITE)
                return evaluateTerminal(node);
            else
                return -evaluateTerminal(node);
        }
        else
            return -NegaScout(node, next_player, depth, -beta, -alpha, stage, true);
    }
    // otherwise 
    for (int i = 0; i < _mCount; i++)
    {
        gameState child = applyMove(node, player, _moves[i], _effects[_moves[i]]);
        
        int score = -NegaScout(child, next_player, depth - 1, -b, -alpha, stage + 1, false);
        
        if (alpha < score && score < beta && i != 0)
        {
            score = -NegaScout(child, next_player, depth - 1, -beta, -alpha, stage + 1, false);
        }
        
        if (score > alpha)
        {
            alpha = score;
        }
        
        if (alpha >= beta)
        {
            return alpha;
        }
        
        b = alpha + 1;
    }
    
    return alpha;
}

/* choose the best move 
   #in#  b          ... current board state
   #in#  player     ... current player
   return           ... best move (x, y) in the format x*10 + y             */
int chooseMove(Board & b, BoardState player, int depth, int end_game)
{
    int _moves[100], _best_move, _mCount, _stage;
    BitBoard _effects[100];
    gameState _gs = parseBoard(b, BLOCKED_MAP);
    
    _mCount = getMoves(_gs, BLOCKED_MAP, player, _moves, _effects);
    _stage = BitCount(_gs.white | _gs.black) - 5;
    
    if (_stage >= 91 - end_game)
    {
        depth = end_game;
    }
    
    int alpha = -INFINITY;
    int beta = INFINITY;
    BoardState next_player = player == WHITE ? BLACK : WHITE;
    
    AGENT_PLAYER = player;
    
    
    for (int i = 0; i < _mCount; i++)
    {
        gameState child = applyMove(_gs, player, _moves[i], _effects[_moves[i]]);
        
        int value = -NegaScout(child, next_player, depth - 1, -beta, -alpha, _stage + 1, false);
        
        if (alpha < value)
        {
            _best_move = _moves[i];
            alpha = value;
        }
        if (alpha >= beta)
            break;
    }

	return _best_move;
}

void initialiseAI()
{
    initialiseMasks();
    initialiseEvaluator();
}
